import {BaseSession} from "./base";
import {BytesValue, fromAny, Int32Value, StringValue, toAny} from "../proto";
import pako from "pako"

const ERR_CODE = 404;

class RequestsSession extends BaseSession {
    constructor(render: HTMLElement) {
        super('Requests', render);
    }

    onCreate(sessionId: number, futureId: number, ...argv) {
        super.onCreate(sessionId, futureId, ...argv);
    }

    onDestroy() {
        super.onDestroy();
    }

    async onReceive(name: String, sessionId: number, futureId: number, ...argv) {
        switch (name) {
            case "get":
                await this.onGet(sessionId, futureId, ...argv);
                return;
            case "post":
                await this.onPost(sessionId, futureId, ...argv);
                return;
            default:
                super.onReceive(name, sessionId, futureId, ...argv);
        }
    }

    private async onGet(sessionId: number, futureId: number, ...argv: any[]) {
        const url = fromAny(argv[0], StringValue);
        const headers = fromAny(argv[1], StringValue);

        const response = await fetch(url, {headers: new Headers(JSON.parse(headers))})
        await this.startRequest(response, sessionId, futureId)
    }

    private async onPost(sessionId: number, futureId: number, ...argv: any[]) {
        const url = fromAny(argv[0], StringValue);
        const headers = fromAny(argv[1], StringValue);
        const data = fromAny(argv[2], StringValue);

        const response = await fetch(url, {
            method: "POST",
            headers: new Headers(JSON.parse(headers)),
            body: data
        });
        await this.startRequest(response, sessionId, futureId)
    }

    private async startRequest(response: Response, sessionId: number, futureId: number) {
        const send = (code: number, headers: string, body: Uint8Array)  => {
            this.sendAction("onResult", sessionId, futureId,
                toAny(code, Int32Value),
                toAny(headers, StringValue),
                toAny(body, BytesValue))
        }

        try {
            const headers = this.headersToJSON(response.headers)
            const buf = await this.getBuffer(response)
            send(response.status, headers, buf)
        } catch (e) {
            send(ERR_CODE, "{}", new Uint8Array(0))
        }
    }

    private async getBuffer(response: Response) {
        const reader = response.body.getReader();
        const chunks = [];
        let totalLength = 0;
        while (true) {
            const {done, value} = await reader.read();
            if (done) break;
            chunks.push(value);
            totalLength += value.length;
        }
        let buffer = new Uint8Array(totalLength);
        let offset = 0;
        for (let chunk of chunks) {
            buffer.set(chunk, offset);
            offset += chunk.length;
        }

        return pako.gzip(buffer)
    }

    private headersToJSON(headers: Headers) {
        const obj = {};
        headers.forEach((value, key) => {
            obj[key] = value;
        });
        return JSON.stringify(obj);
    }
}

export {RequestsSession}